package cn.yogehaoren.config;

import cn.yogehaoren.annotation.PropertyFrom;
import com.haojiangbo.config.ClientCheckConfig;
import com.haojiangbo.inteface.Container;
import com.haojiangbo.model.ConfigModel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.yaml.snakeyaml.Yaml;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author WangNing yogehaoren@gamil.com <br>
 * @since 1.0
 */
@Data
@ToString
@Slf4j
@NoArgsConstructor
@SuppressWarnings({"unchecked"})
public class ServerConfig implements Container {

    @PropertyFrom("server.host")
    private String bridgeHost;

    @PropertyFrom("server.port")
    private Integer bridgePort;

    @PropertyFrom("server.limit")
    private Integer limitClientByteSize =1024;

    @PropertyFrom("proxy.client")
    private List<ConfigModel> clientConfig;

    private volatile static ServerConfig INSTANCE = null;

    public static ServerConfig INSTANCE() {
        if(INSTANCE == null){
            synchronized (ServerConfig.class){
                if(INSTANCE != null){
                    return INSTANCE;
                }else {

                    INSTANCE = new ServerConfig();
                    Yaml yaml = new Yaml();
                    InputStream configYaml = null;

                    try{
                        configYaml = new FileInputStream("./server-config.yaml");
                    }catch (IOException e){
                        log.warn("无法找到配置文件, 使用默认配置文件");

                    }

                    if(configYaml==null){
                        configYaml = ServerConfig.class.getClassLoader().getResourceAsStream("server-config.yaml");

                    }


                    Map<String, Object> configMap = yaml.load(configYaml);

                    Class<ServerConfig> configClazz = ServerConfig.class;

                    Field[] declaredFields = configClazz.getDeclaredFields();

                    for (Field field: declaredFields){

                        PropertyFrom annotation = field.getAnnotation(PropertyFrom.class);
                        if(annotation != null){

                            String from = annotation.value();
                            if(!"".equals(from)){

                                if(field.getName().equals("clientConfig")){
                                    INSTANCE.setClientConfig(getProperties(configMap, from, ConfigModel.class));
                                    continue;
                                }

                                Object propertyValue = ServerConfig.getProperty(configMap, from);
                                if(propertyValue == null){continue;}
                                field.setAccessible(true);
                                try {
                                    field.set(INSTANCE, propertyValue);
                                }catch (IllegalAccessException e){

                                    log.warn("cannot load properties:{}", from);
                                }
                            }
                        }
                    }

                }
            }
            INSTANCE.initConfig();
        }

        return INSTANCE;

    }

    /**
     * 从Map中抽取配置文件
     * @param configMap -
     * @param properties -
     * @return -
     */
    private static Object getProperty(Map<String, Object> configMap, String properties){
        String[] split = properties.split("\\.");

        Map<String, Object> cache = configMap;
        for (int i = 0; i < split.length; i++) {

            if(i==split.length-1){
                return cache.get(split[i]);
            }else {
                cache = (Map<String, Object>)cache.get(split[i]);
            }
        }
        return null;
    }

    private static <T> List<T> getProperties(Map<String, Object> configMap, String properties, Class<T> clazz){

        String[] split = properties.split("\\.");

        Map<String, Object> cache = configMap;
        List<T> result = new ArrayList<>();
        for (int i = 0; i < split.length; i++) {

            if(i==split.length-1){

                List<Map<String, Object>> configList = (List<Map<String, Object>>)cache.get(split[i]);
                configList.forEach(config->{

                    try {
                        T configObject = clazz.newInstance();
                        config.forEach((k,v)->{

                            try {
                                Field field = clazz.getDeclaredField(k);
                                field.setAccessible(true);
                                if(field.getType() == String.class){
                                    field.set(configObject, v.toString());
                                }else {
                                    field.set(configObject, v);
                                }
                            } catch (NoSuchFieldException | IllegalAccessException e) {
                                log.warn("{} can not set field: {} ", clazz.getName(), k);
                            }

                        });
                        result.add(configObject);
                    } catch (Exception e) {
                        log.warn("cannot create new Instance: {}", clazz.getName());
                    }

                });

            }else {
                cache = (Map<String, Object>)cache.get(split[i]);
            }
        }
        return result;

    }

    @Override
    public void start() {
        synchronized (ServerConfig.class){
            INSTANCE = null;
            INSTANCE();
        }
    }

    @Override
    public void stop() {

    }

    private void initConfig(){
        if(clientConfig!=null&&clientConfig.size()!=0){
            clientConfig.forEach(v->{
                ClientCheckConfig.CLIENT_CHECK_MAP.put(v.getClientId(), v);
            });
        }
    }

}
